App.require(["htmlTool", "canvasTool"], function (htmlTool, canvasTool) {
    var app = App.getCurrent();

    app.defineLayer("str-pie-chart", {
        render: function (next) {
            this.initProperty();
            this.data.isChange = true;
            this.data.isMouseIn = false;
            this.fetch("./index.html", function (text) {
                next(this.renderHTML(text));
            })
        },
        removeOption: function (index) {
            this.data.options.splice(index, 1);
            this.data.isChange = true;
            this.computeRatio();
            var property = this.property;
            if (property.animation) this.addPropertyAnimation(property.animation, this);
            else this.addChange();
        },
        appendOption: function (option) {
            this.data.options.push(option);
            this.data.isChange = true;
            this.computeRatio();
            var property = this.property;
            if (property.animation) this.addPropertyAnimation(property.animation, this);
            else this.addChange();
        },
        insertBefore: function (index, option) {
            this.data.options.splice(index, 0, option);
            this.data.isChange = true;
            this.computeRatio();
            var property = this.property;
            if (property.animation) this.addPropertyAnimation(property.animation, this);
            else this.addChange();
        },
        changeOption: function (index, opt) {
            var option = this.data.options[index];
            if (!option) return;
            this.data.isChange = true;
            this.computeRatio();
            this.addChange(opt, option);
        },
        initProperty: function () {
            var property = this.property;

            property.id = property.id || this.uid;

            this.data.attributeStr = this._createPropertyStr(property);
            this.data.options = htmlTool.getOptions(this.slot);
            property.mark = typeof property.mark === "undefined" ? false : true;
            this.computeRatio();
            if (property.animation) this.addPropertyAnimation(property.animation, this);
        },
        get mark() {
            return this.getValue("mark");
        },
        set mark(value) {
            this.setValue("mark", value);
        },
        get markfill() {
            return this.getValue("markfill");
        },
        set markfill(value) {
            this.setValue("markfill", value);
        },
        get markoffset() {
            return this.getValue("markoffset");
        },
        set markoffset(value) {
            this.setValue("markoffset", value);
        },
        get markradius() {
            return this.getValue("markradius");
        },
        set markradius(value) {
            this.setValue("markradius", value);
        },
        get or() {
            return this.getValue("or");
        },
        set or(value) {
            this.setValue("or", value);
        },
        get ir() {
            return this.getValue("ir");
        },
        set ir(value) {
            this.setValue("ir", value);
        },
        get fill() {
            return this.getValue("fill");
        },
        set fill(value) {
            this.setValue("fill", value);
        },
        get stroke() {
            return this.getValue("stroke");
        },
        set stroke(value) {
            this.setValue("stroke", value);
        },
        get linedash() {
            return this.getValue("linedash");
        },
        set linedash(value) {
            this.setValue("linedash(", value);
        },
        get linedashoffset() {
            return this.getValue("linedashoffset");
        },
        set linedashoffset(value) {
            this.setValue("linedashoffset", value);
        },
        get linewidth() {
            return this.getValue("linewidth");
        },
        set linewidth(value) {
            this.setValue("linewidth", value);
        },
        get transform() {
            return this.getValue("transform");
        },
        set transform(value) {
            this.setValue("transform", value);
        },
        get shadow() {
            return this.getValue("shadow");
        },
        set shadow(value) {
            this.setValue("shadow", value);
        },
        get hidden() {
            return this.getValue("hidden");
        },
        set hidden(value) {
            this.setValue("hidden", value);
        },
        get x() {
            return this.getValue("x");
        },
        set x(value) {
            this.setValue("x", value);
        },
        get y() {
            return this.getValue("y");
        },
        set y(value) {
            this.setValue("y", value);
        },
        get startoffset() {
            return this.getValue("startoffset");
        },
        set startoffset(value) {
            this.setValue("startoffset", value);
        },
        get direction() {
            return this.getValue("direction");
        },
        set direction(value) {
            this.setValue("direction", value);
        },
        get max() {
            return this.getValue("max");
        },
        set max(value) {
            this.setValue("max", value);
        },
        get animation() {
            return this.getValue("animation");
        },
        set animation(value) {
            if (this.setValue("animation", value)) {
                if (value) this.addPropertyAnimation(value, this);
            }
        },
        set transition(value) {
            this.setValue("transition", value);
        },
        get transition() {
            return this.getValue("transition");
        },
        set opacity(value) {
            this.setValue("opacity", value);
        },
        get opacity() {
            return this.getValue("opacity");
        },
        computeRatio: function () {
            var sum = 0, offsets = [], options = this.data.options;
            for (var i = 0; i < options.length; i++) {
                offsets.push(sum);
                sum += parseFloat(options[i].value);
            }
            var ratioList = [];
            for (var i = 0; i < offsets.length; i++) {
                ratioList.push({
                    offset: offsets[i] / sum,
                    current: parseFloat(options[i].value) / sum
                });
            }
            this.data.ratioList = ratioList;
        },
        drawSelf: function (ctx, ratio) {
            if (this.hidden || this.data.options.length == 0) return;
            var property = this.property;
            var dObj = this.getPropertyUse(["opacity", "x", "y", "shadow", "or", "ir", "fill", "stroke", "linedash", "linedashoffset", "linewidth", "startoffset", "max"]);
            if (typeof dObj.opacity !== "undefined") ctx.globalAlpha = parseFloat(dObj.opacity);
            this.transformView(ctx, ratio);
            var keyToCanvas = canvasTool.keyToCanvas;
            for (var key in property) {
                var ctxKey = keyToCanvas[key];
                if (ctxKey) {
                    if (ctxKey == "fillStyle" || ctxKey == "strokeStyle") continue;
                    else ctx[ctxKey] = this[key];
                }
            }

            if (dObj.linedash) this.useLineDash(ctx, dObj.linedash, dObj.linedashoffset, ratio);
            if (dObj.shadow) this.useShadow(ctx, dObj.shadow, ratio);

            var options = this.data.options;
            var ratioList = this.data.ratioList;
            ctx.translate(dObj.x * ratio, dObj.y * ratio);
            for (var i = 0; i < options.length; i++) {
                var obj = options[i], ratioObj = ratioList[i];
                this.createPath(ctx, obj, ratioObj, ratio, dObj);
                ctx.save();
                if (obj.shadow) this.useShadow(ctx, obj.shadow, ratio);
                if (obj.fill || dObj.fill) {
                    ctx.fillStyle = this.getFillOrStrokeStyle(obj.fill || dObj.fill);
                    ctx.fill();
                }
                if (obj.stroke || dObj.stroke) {
                    ctx.strokeStyle = this.getFillOrStrokeStyle(obj.stroke || dObj.stroke);
                    ctx.stroke();
                }
                if (obj.marker) {
                    var marker = this.getMarkerByStr(obj.marker, this.ratio);
                    if (marker) {
                        ctx.save();
                        var markerPos = this._getMarkerPos(obj, ratioObj, ratio, dObj);
                        ctx.translate(markerPos.x, markerPos.y);
                        marker.drawToCanvas(ctx, ratio);
                        ctx.restore();
                    }
                }
                ctx.restore();
            }
            var mark = this.mark, 
                markOffset = this.markoffset? parseFloat(this.markoffset) : 0.5, 
                markFont = this.markfont,
                markfill = this.markfill;
            if (mark) {
                if (markfill) ctx.fillStyle = this.getFillOrStrokeStyle(markfill);
                if (markFont) ctx.font = this.computeFont(markFont, ratio);
                ctx.textAlign = "center";
                ctx.textBaseline = "middle";
                for (var i = 0; i < options.length; i++) {
                    var obj = options[i], ratioObj = ratioList[i];
                    if (!obj.content) continue;
                    var pos = this.getMarkPos(obj, ratioObj, ratio, obj, markOffset || obj.offset);
                    ctx.save();
                    ctx.translate(pos.x, pos.y);
                    if (obj.font) ctx.font = this.computeFont(obj.font, ratio);
                    if (obj.align) ctx.textAlign = obj.align;
                    if (obj.baseline) ctx.textBaseline = obj.baseline;
                    if (obj.markfill) ctx.fill = this.getFillOrStrokeStyle(obj.markfill);
                    ctx.fillText(obj.content, 0, 0);
                    ctx.restore();
                }
            }
        },
        getMarkPos: function (obj, ratioObj, ratio, dObj, offset) {
            var theta = (ratioObj.offset + ratioObj.current * parseFloat(offset)) * (dObj.max || Math.PI * 2) + (dObj.startoffset || -Math.PI / 2);
            var r = parseFloat(obj.or) / 2 + parseFloat(obj.ir) / 2;
            return {
                x: r * Math.cos(theta), y: r * Math.sin(theta)
            }
        },
        _getMarkerPos: function (obj, ratioObj, ratio, dObj) {
            var offset = (ratioObj.offset + ratioObj.current * parseFloat(obj.moffset)) * (dObj.max || Math.PI * 2)+ (dObj.startoffset || -Math.PI / 2);
            var r = parseFloat(obj.mr);
            return {
                x: r * Math.cos(offset), y: r * Math.sin(offset)
            }
        },
        createPath: function (ctx, obj, ratioObj, ratio, dObj) {
            var dObj = dObj || this;
            var offset = ratioObj.offset * dObj.max,
            duration = ratioObj.current * dObj.max;

            var start = dObj.startoffset + (this.direction == "CW" ? offset : -offset);
            var end = start + (this.direction == "CW" ? duration : -duration);
            var rx = 0, ry = 0;
            if (obj.offset) {
                var middle = (start + end) / 2;
                rx = Math.cos(middle) * obj.offset * ratio;
                ry = Math.sin(middle) * obj.offset * ratio;
            }
            
            ctx.beginPath();
            var ir = parseFloat(obj.ir || dObj.ir), or = parseFloat(obj.or || dObj.or);
            if (dObj.ir) {
                ctx.moveTo(rx + or * Math.cos(start) * ratio, ry + or * Math.sin(start) * ratio);
                ctx.arc(rx, ry, or * ratio, start, end, this.direction !== "CW");
                ctx.lineTo(rx + ir * Math.cos(end) * ratio, ry + ir * Math.sin(end) * ratio);
                ctx.arc(rx, ry, ir * ratio, end, start, this.direction === "CW");
            }
            else {
                ctx.moveTo(rx, ry);
                ctx.lineTo(rx + or * Math.cos(start) * ratio, ry + or * Math.sin(start) * ratio);
                ctx.arc(rx, ry, or * ratio, start, end, this.direction !== "CW");
            }
            
            ctx.closePath();
        },
        dispose: function () {
            this.removeAnimation();
            this.data.isChange = true;
            this.data.isMouseIn = false;
        },
        clickLayerHandler: function (ev, ctx, ratio) {
            var options = this.data.options;
            var ratioList = this.data.ratioList;
            ctx.translate(this.x * ratio, this.y * ratio);
            for (var i = 0; i < options.length; i++) {
                var obj = options[i], ratioObj = ratioList[i];
                this.createPath(ctx, obj, ratioObj, ratio);
                if (ctx.isPointInPath(ev.clientX * ratio, ev.clientY * ratio)) {
                    this.dispatchCustomEvent("piechartclick", {
                        component: this,
                        event: ev,
                        index: i,
                        data: obj,
                        ratioObj: ratioObj
                    });
                    break;
                }
            }
        },
        moveLayerHandler: function (ev, ctx, ratio) {
            var options = this.data.options;
            var ratioList = this.data.ratioList, isMouseIn = false;
            ctx.translate(this.x * ratio, this.y * ratio);
            for (var i = 0; i < options.length; i++) {
                var obj = options[i], ratioObj = ratioList[i];
                this.createPath(ctx, obj, ratioObj, ratio);
                if (ctx.isPointInPath(ev.clientX * ratio, ev.clientY * ratio)) {
                    isMouseIn = true;
                    if (!this.data.isMouseIn) {
                        this.data.isMouseIn = true;
                        this.data.index = i;
                        this.dispatchCustomEvent("piechartmouseenter", {
                            component: this,
                            event: ev,
                            index: i,
                            data: obj,
                            ratioObj: ratioObj
                        });
                        break;
                    }
                }
            }

            if (this.data.isMouseIn && !isMouseIn) {
                this.data.isMouseIn = false;
                this.dispatchCustomEvent("piechartmouseleave", {
                    component: this,
                    event: ev,
                    index: this.data.index,
                    data: options[this.data.index],
                    ratioObj: ratioList[this.data.index]
                });
            }
        },
    })
});